Перейти к основному содержимому

Управляющие конструкции и циклы в PHP

Разработчику Архитектору

Операторы и циклы

PHP предоставляет полный набор управляющих конструкций, позволяющих строить сложную логику выполнения программ. Эти конструкции делятся на две большие группы: условные (ветвление) и циклические (повторение). Внутри этих групп реализованы как классические операторы (if, for), так и современные языковые возможности (match, генераторы).

Все управляющие конструкции работают с выражениями, результат которых интерпретируется в логическом контексте. В PHP любое значение может быть преобразовано к булевому типу по правилам неявного приведения: например, 0, '', [], null, false считаются ложными, всё остальное — истинным.


Условные конструкции

Конструкция if

Конструкция if позволяет выполнять блок кода только при истинности заданного условия.

if (<условие>) {
// выполняется, если условие истинно
} elseif (<другое_условие>) {
// выполняется, если предыдущее ложно, а это — истинно
} else {
// выполняется, если все условия ложны
}
<?php
$temperature = 22;

if ($temperature > 30) {
echo "Жарко";
} elseif ($temperature > 20) {
echo "Комфортно";
} elseif ($temperature > 10) {
echo "Прохладно";
} else {
echo "Холодно";
}

Конструкция поддерживает неограниченное количество веток elseif. Проверка условий происходит строго сверху вниз. Первое истинное условие определяет выполняемый блок; остальные игнорируются.

При работе с внешними данными (например, из $_GET) рекомендуется использовать строгую проверку:

$id = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT);
if ($id !== false && $id > 0) {
loadItem($id);
}

Это предотвращает ошибки, связанные с неявным приведением типов.


Альтернативный синтаксис шаблонов

В HTML-шаблонах допустим альтернативный синтаксис, повышающий читаемость:

<?php if (<условие>): ?>
<!-- HTML -->
<?php elseif (<условие>): ?>
<!-- другой HTML -->
<?php else: ?>
<!-- резервный HTML -->
<?php endif; ?>
<?php if ($isLoggedIn): ?>
<div>Привет, <?= htmlspecialchars($user) ?>!</div>
<?php else: ?>
<form method="post" action="/login">
<input name="user">
<button>Войти</button>
</form>
<?php endif; ?>

Этот стиль применяется только в смешанных файлах (HTML + PHP). В чистом коде используется фигурный синтаксис.


Конструкция switch

Конструкция switch сравнивает выражение с набором значений и выполняет соответствующий блок кода.

switch (<выражение>) {
case <значение1>:
// действия
break;
case <значение2>:
// действия
break;
default:
// действия по умолчанию
}
<?php
$status = 404;

switch ($status) {
case 200:
echo "OK";
break;
case 404:
echo "Страница не найдена";
break;
case 500:
echo "Ошибка сервера";
break;
default:
echo "Неизвестный статус";
}

Особенности switch:

  • Использует слабое сравнение (==). Это означает, что '2' == 2true.
  • Требует явного указания break в конце каждой ветки. Без него выполнение «проваливается» в следующую ветку.
  • Поддерживает ветку default для обработки всех остальных случаев.
  • Может содержать составные инструкции: переменные, циклы, вызовы функций.

Пример проваливания:

switch ($role) {
case 'admin':
grantFullAccess();
// нет break — продолжаем
case 'editor':
grantEditAccess();
break;
default:
grantReadAccess();
}

Такой паттерн допустим, но требует комментариев.


Оператор match (PHP 8.0+)

Оператор match — функциональная альтернатива switch. Он всегда возвращает значение, использует строгое сравнение (===) и не допускает проваливания.

$<результат> = match (<выражение>) {
<значение1> => <результат1>,
<значение2>, <значение3> => <результат2>,
default => <резервный_результат>
};
<?php
$role = 'editor';
$label = match($role) {
'admin' => 'Администратор',
'editor' => 'Редактор',
'user' => 'Пользователь',
default => 'Гость'
};

// Сравнение нескольких значений
$message = match($code) {
200, 201, 204 => 'Успех',
400, 404 => 'Ошибка клиента',
default => 'Неизвестно'
};

Преимущества match:

  • Компактность и безопасность.
  • Возможность использования внутри выражений.
  • Гарантированное покрытие всех случаев (иначе — исключение UnhandledMatchError).

Ограничения:

  • Каждая ветка — одно выражение, без составных инструкций.
  • Не подходит для побочных эффектов (например, логирования).

Логические и тернарные операторы

Тернарный оператор

Тернарный оператор позволяет компактно записать простое условное присваивание.

$<результат> = <условие> ? <значение_если_истина> : <значение_если_ложь>;
$status = ($age >= 18) ? 'взрослый' : 'несовершеннолетний';

Сокращённая форма (Elvis operator):

$<результат> = <выражение> ?: <значение_по_умолчанию>;
$username = $_GET['user'] ?: 'Гость';

Здесь $username получит значение $_GET['user'], если оно истинно, иначе — 'Гость'.


Оператор null coalescing (??)

Оператор ?? проверяет значение на строгое равенство null.

$<результат> = $<возможно_null> ?? <значение_по_умолчанию>;
$page = $_GET['page'] ?? 1;

Если $_GET['page'] равно null, будет использовано 1. Значения 0, '0', '' считаются валидными и не заменяются.

Цепочка:

$value = $a ?? $b ?? $c ?? 'default';

Null coalescing assignment (??=)

Начиная с PHP 7.4:

$config['timeout'] ??= 30;

Эквивалентно:

$config['timeout'] = $config['timeout'] ?? 30;

Циклические конструкции

Цикл for

Цикл for используется, когда известно количество итераций или требуется числовая индексация.

for ($<счётчик> = <начало>; $<счётчик> < <конец>; $<счётчик>++) {
// тело цикла
}
for ($i = 0; $i < 5; $i++) {
echo "Итерация $i\n";
}

Компоненты:

  • Инициализация — один раз перед циклом.
  • Условие — проверяется перед каждой итерацией.
  • Инкремент — выполняется после каждой итерации.

Переменная цикла остаётся в области видимости после завершения.


Цикл while

Цикл while проверяет условие перед каждой итерацией.

while (<условие>) {
// тело цикла
}
$count = 3;
while ($count > 0) {
echo "Осталось: $count\n";
$count--;
}

Если условие изначально ложно, тело не выполняется ни разу.


Цикл do-while

Цикл do-while гарантирует выполнение тела хотя бы один раз.

do {
// тело цикла
} while (<условие>);
$input = '';
do {
$input = readline('Введите число от 1 до 10: ');
$number = (int)$input;
} while ($number < 1 || $number > 10);

Обязательна точка с запятой после while.


Цикл foreach

Цикл foreach предназначен для итерации по массивам и объектам.

По значениям:

foreach ($<массив> as $<элемент>) {
// обработка элемента
}

По ключам и значениям:

foreach ($<массив> as $<ключ> => $<значение>) {
// обработка пары
}
$scores = ['Анна' => 95, 'Борис' => 87];
foreach ($scores as $name => $score) {
echo "$name: $score\n";
}

Модификация через ссылку:

foreach ($<массив> as &$<элемент>) {
$<элемент> = <новое_значение>;
}
unset($<элемент>); // обязательный сброс ссылки
$prices = [100, 200];
foreach ($prices as &$price) {
$price *= 1.1;
}
unset($price);

Управление потоком выполнения

Операторы break и continue

Оператор break прекращает выполнение цикла.
Оператор continue переходит к следующей итерации.

Оба принимают необязательный числовой аргумент — количество уровней вложенности для выхода/пропуска:

for ($i = 0; $i < 3; $i++) {
for ($j = 0; $j < 3; $j++) {
if ($i === 1 && $j === 1) {
break 2; // выход из обоих циклов
}
echo "$i,$j ";
}
}

Генераторы и yield

Генераторы позволяют создавать ленивые последовательности без загрузки всех данных в память.

function xrange($start, $end) {
for ($i = $start; $i <= $end; $i++) {
yield $i;
}
}

foreach (xrange(1, 5) as $n) {
echo "$n ";
}

Генератор с ключами:

yield $key => $value;

Генератор можно итерировать только один раз. Для повторного использования вызывается заново.


Рекомендации

  • Используйте === и !== для строгого сравнения.
  • Предпочитайте match вместо switch при выборе значения.
  • Избегайте модификации массива внутри foreach.
  • Всегда вызывайте unset() после foreach (&$ref).
  • Валидируйте входные данные до условий.
  • Не используйте and/or — их приоритет ниже ожидаемого.